package club.jdiy.dev.helper;

import club.jdiy.core.AdminContext;
import club.jdiy.core.base.domain.Pager;
import club.jdiy.core.ex.JDiyFormException;
import club.jdiy.dev.types.InputTpl;
import club.jdiy.dev.types.ManyTpl;
import club.jdiy.dev.meta.Opts;
import club.jdiy.dev.meta.InputMeta;
import club.jdiy.dev.types.OptTpl;
import club.jdiy.dev.view.FtlParser;
import club.jdiy.core.sql.Args;
import club.jdiy.core.sql.Dao;
import club.jdiy.core.sql.Rs;
import club.jdiy.utils.StringUtils;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.*;
import java.util.regex.Pattern;

@Component
public class OptsHelper {
    private static final Pattern static_item = Pattern.compile("^([^:\n]*:[^:\n]*)(\n([^:\n]*:[^:\n]+))*\n*$");

    public static void designValidate(Opts vo, String target) {
        if (StringUtils.hasText(vo.getItems())) {
            if (!static_item.matcher(vo.getItems()).matches()) {
                throw new JDiyFormException("选中的控件[静态条目]格式输入不正确，必须是“A:B”的格式(A为选项值，B为显示文字，一行填一个", target);
            }
        } else if (vo.getOptType() == null) {
            throw new JDiyFormException("选中的控件[静态条目]或[动态条目]至少需要配置一个，否则该控件将无可选条目项。", target);
        }
        if (vo.getOptType() != null) {
            switch (vo.getOptType()) {
                case table:
                    if (StringUtils.isEmpty(vo.getOptTable()))
                        throw new JDiyFormException("选中的控件动态条目[数据库表]未配置。", target);
                    if (vo.getOptTxtField() == null || "".equals(vo.getOptTxtField()))
                        throw new JDiyFormException("选中的控件动态条目[显示字段]未配置。", target);
                    if (vo.getOptValField() == null || "".equals(vo.getOptValField()))
                        throw new JDiyFormException("选中的控件动态条目[值字段]未配置。", target);
                    break;
                case entity:
                    if (StringUtils.isEmpty(vo.getOptEntity()))
                        throw new JDiyFormException("选中的控件动态条目[JPA实体]未配置。", target);
                    if (vo.getOptTxtField() == null || "".equals(vo.getOptTxtField()))
                        throw new JDiyFormException("选中的控件动态条目[显示字段]未配置。", target);
                    if (vo.getOptValField() == null || "".equals(vo.getOptValField()))
                        throw new JDiyFormException("选中的控件动态条目[值字段]未配置。", target);
                    break;
                case dict:
                    if (StringUtils.isEmpty(vo.getOptDict()))
                        throw new JDiyFormException("选中的控件动态条目字典未配置。", target);
                    break;
                case jql:
                case sql:
                    if (StringUtils.isEmpty(vo.getOptQry()))
                        throw new JDiyFormException("请填写动态条目数据的" + vo.getOptType().name().toUpperCase() + "查询语句。", target);
                    break;
            }
        }
    }

    public Collection<Entry> listOptions(Opts it, FtlParser parser) {
        Collection<Entry> opts = new LinkedHashSet<>();
        //静态：
        addStaticOptions(opts, it);
        //动态(entity || table):
        addETOptions(opts, it, it.getOptValField(), parser);
        //动态(dict):
        addDictOptions(opts, it);
        //动态(sql/jql):
        addQLOptions(opts, it, parser);
        return opts;
    }

    public Collection<Entry> listOptions(InputMeta it, FtlParser parser) {
        Collection<Entry> opts = new LinkedHashSet<>();
        boolean isManyUpdate = InputTpl.checkbox == it.getType()
                && it.getManyType() != null && ManyTpl.none != it.getManyType();
        if (!isManyUpdate) {
            //静态：
            addStaticOptions(opts, it);
            //动态(dict):
            addDictOptions(opts, it);
            //动态(sql/jql):
            addQLOptions(opts, it, parser);
        }
        //动态(entity || table):
        addETOptions(opts, it, isManyUpdate
                ? context.getDao().getTableInfo(it.getOptTable()).getPrimaryKey()
                : it.getOptValField(), parser);
        return opts;
    }


    private void addStaticOptions(Collection<Entry> opts, Opts it) {
        if (StringUtils.hasText(it.getItems())) {
            String[] sa = it.getItems().trim().split("\n");
            Arrays.stream(sa).filter(ss -> !"".equals(ss.trim())).map(ss -> (ss + " ").split(":")).forEach(sb -> {
                if (sb.length >= 2) opts.add(new Entry(sb[0].trim(), sb[1].trim()));
                else opts.add(new Entry(sb[0].trim(), sb[0].trim()));
            });
        }
    }

    private void addETOptions(Collection<Entry> opts, Opts it, String valueFieldName, FtlParser parser) {
        try {
            if (it.getOptType() == OptTpl.entity) {

                String hql = String.format("select new club.jdiy.dev.helper.OptsHelper.Entry(concat(o.%s,''), concat(o.%s,'')) from %s o%s%s",
                        valueFieldName,
                        it.getOptTxtField(),
                        it.getOptEntity(),
                        StringUtils.isEmpty(it.getOptWhere()) ? "" : " where " + parser.parse(it.getOptWhere()),
                        StringUtils.isEmpty(it.getOptSort()) ? "" : " order by " + it.getOptSort());
                opts.addAll(em.createQuery(hql, Entry.class).setMaxResults(300).getResultList());

            } else if (it.getOptType() == OptTpl.table) {
                Dao dao = context.getDao();
                Pager<Rs> ls1 = dao.ls(new Args(it.getOptTable() + " o",
                        (StringUtils.isEmpty(it.getOptWhere()) ? "" : parser.parse(it.getOptWhere())) +
                                (StringUtils.isEmpty(it.getOptSort()) ? "" : " order by " + it.getOptSort()),
                        300, 1,
                        "o." + valueFieldName + " as v, o." + it.getOptTxtField() + " as k"));
                ls1.getItems().stream().map(rs -> new Entry(rs.getString("v"), rs.getString("k"))).forEach(opts::add);

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private void addDictOptions(Collection<Entry> opts, Opts it) {
        if (it.getOptType() == OptTpl.dict) {
            context.getDict(it.getOptDict()).ifPresent(m -> {
                m.forEach((k, v) -> opts.add(new Entry(k, v.getName())));
            });
        }
    }

    private void addQLOptions(Collection<Entry> opts, Opts it, FtlParser parser) {
        try {
            if (it.getOptType() == OptTpl.jql) {
                em.createQuery(parser.parse(it.getOptQry()), Object[].class).getResultList().stream()
                        .map(ox -> new Entry(ox[0].toString(), ox[ox.length < 2 ? 0 : 1].toString())).forEach(opts::add);
            } else if (it.getOptType() == OptTpl.sql) {
                Dao dao = context.getDao();
                Pager<Rs> ls2 = dao.ls(parser.parse(it.getOptQry()), 300, 1);
                int textIndex = -1;
                for (Rs rs : ls2.getItems()) {
                    if (textIndex == -1) textIndex = rs.keySet().size() < 2 ? 1 : 2;
                    opts.add(new Entry(rs.getString(1), rs.getString(textIndex)));
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Data
    @NoArgsConstructor
    public static class Entry {
        private String v;
        private String k;

        public Entry(String v, String k) {
            this.v = v;
            this.k = k;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Entry entry = (Entry) o;
            return Objects.equals(v, entry.v);
        }

        @Override
        public int hashCode() {
            return Objects.hash(v);
        }
    }

    @Resource
    private AdminContext context;
    @PersistenceContext
    private EntityManager em;
}
